package com.fs.mybatis.controller;

import static com.fs.mybatis.core.generator.internal.util.StringUtility.stringHasValue;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fs.mybatis.core.generator.config.CommentGeneratorConfiguration;
import com.fs.mybatis.core.generator.config.Context;
import com.fs.mybatis.core.generator.config.GeneratedKey;
import com.fs.mybatis.core.generator.config.JDBCConnectionConfiguration;
import com.fs.mybatis.core.generator.config.JavaClientGeneratorConfiguration;
import com.fs.mybatis.core.generator.config.JavaModelGeneratorConfiguration;
import com.fs.mybatis.core.generator.config.JavaTypeResolverConfiguration;
import com.fs.mybatis.core.generator.config.SqlMapGeneratorConfiguration;
import com.fs.mybatis.core.generator.config.TableConfiguration;
import com.fs.mybatis.core.generator.exception.ShellException;
import com.fs.mybatis.domain.Table;
import com.fs.mybatis.service.CodeService;
import com.fs.mybatis.util.Constants;
import com.fs.mybatis.util.StringUtils;
import com.fs.mybatis.util.ZipUtil;
import com.google.common.collect.Maps;

@Controller
public class CodeController {

    @Autowired
    private CodeService codeService;

    private boolean flag = true;

    private File file;

    @RequestMapping(value = "/getTargetDatabaseTables.action")
    @ResponseBody
    public List<Table> getConnection(HttpServletRequest request, String url, String usr, String pas)
            throws ClassNotFoundException, SQLException {
        return codeService.getTargetDatabaseTables(url, usr, pas);

    }

    @RequestMapping(value = "/getNames.action")
    @ResponseBody
    public Map<String, Object> getNames(HttpServletRequest request, String packagename, String modelname) {
        Map<String, Object> res = Maps.newHashMap();
        String modelName = packagename + "." + Constants.config.getString("domain.name") + "." + modelname + ".XXXX";
        String daoName = packagename + "." + Constants.config.getString("dao.name") + "." + modelname + ".XXXX";
        res.put("modelName", modelName);
        res.put("daoName", daoName);
        return res;

    }

    @RequestMapping(value = "/getCode.action")
    @ResponseBody
    public void getCode(HttpServletRequest request, HttpServletResponse response, String c_url, String c_user,
            String c_pass, String packagename, String modelname, String c_table, String c_style, String c_id)
            throws ClassNotFoundException, SQLException, IOException {
        String targetpath;
        if (File.separator.equals("/")) {
            targetpath = "/home/mybatis";
        } else {
            targetpath = "d:\\mybatis";
        }
        Context context = getContext(c_url, c_user, c_pass, c_table, packagename, modelname, targetpath, c_id);
        Map<File, String> files = null;
        if ("1".equals(c_style)) {
            files = codeService.generalFiles(context);
        } else if ("2".equals(c_style)) {
            files = codeService.readWriteFiles(context);
        } else {
            System.out.println("未选择生产代码方式");
            return;
        }
        writeFile(files);
        try {
            downloadFile(packagename, targetpath, request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void writeFile(Map<File, String> files) throws IOException {
        for (File file : files.keySet()) {
            FileOutputStream fos = new FileOutputStream(file, false);
            OutputStreamWriter osw;
            osw = new OutputStreamWriter(fos, "UTF-8");
            BufferedWriter bw = new BufferedWriter(osw);
            bw.write(files.get(file));
            bw.close();
        }
    }

    private void downloadFile(String packagename, String path, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        setFileDownloadHeader(request, response, packagename + ".zip");
        OutputStream zos = response.getOutputStream();

        String zippath = path + File.separator + "com" + File.separator;
        path = path + File.separator + "com.zip";
        ZipUtil.zip(zippath, path);

        // response读取压缩文件
        File temp = new File(path);
        InputStream in = new FileInputStream(temp);
        byte[] buffer = new byte[1024];
        int r = 0;
        while ((r = in.read(buffer)) != -1) {
            zos.write(buffer, 0, r);
        }
        in.close();
        // 删除压缩文件
        temp.delete();
        DeleteFolder(zippath);
        zos.flush();
        zos.close();
    }

    @SuppressWarnings("unused")
    private File getUniqueFileName(File directory, String fileName) {
        File answer = null;

        // try up to 1000 times to generate a unique file name
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i < 1000; i++) {
            sb.setLength(0);
            sb.append(fileName);
            sb.append('.');
            sb.append(i);

            File testFile = new File(directory, sb.toString());
            if (!testFile.exists()) {
                answer = testFile;
                break;
            }
        }
        return answer;
    }

    public String mergeJavaFile(String newFileSource, String existingFileFullPath, String[] javadocTags,
            String fileEncoding) throws ShellException {
        throw new UnsupportedOperationException();
    }

    /**
     * 根据路径删除指定的目录或文件，无论存在与否
     *
     * @param sPath 要删除的目录或文件
     * @return 删除成功返回 true，否则返回 false。
     */
    public boolean DeleteFolder(String sPath) {
        flag = false;
        file = new File(sPath);
        // 判断目录或文件是否存在
        if (!file.exists()) { // 不存在返回 false
            return flag;
        } else {
            // 判断是否为文件
            if (file.isFile()) { // 为文件时调用删除文件方法
                return deleteFile(sPath);
            } else { // 为目录时调用删除目录方法
                return deleteDirectory(sPath);
            }
        }
    }

    /**
     * 删除单个文件
     * 
     * @param sPath 被删除文件的文件名
     * @return 单个文件删除成功返回true，否则返回false
     */
    public boolean deleteFile(String sPath) {
        flag = false;
        file = new File(sPath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists()) {
            file.delete();
            flag = true;
        }
        return flag;
    }

    /**
     * 删除目录（文件夹）以及目录下的文件
     * 
     * @param sPath 被删除目录的文件路径
     * @return 目录删除成功返回true，否则返回false
     */
    public boolean deleteDirectory(String sPath) {
        // 如果sPath不以文件分隔符结尾，自动添加文件分隔符
        if (!sPath.endsWith(File.separator)) {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        // 如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        flag = true;
        // 删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            // 删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag)
                    break;
            } // 删除子目录
            else {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag)
                    break;
            }
        }
        if (!flag)
            return false;
        // 删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }

    public static void setFileDownloadHeader(HttpServletRequest request, HttpServletResponse response, String fileName) {
        final String userAgent = request.getHeader("USER-AGENT");
        try {
            String finalFileName = null;
            if (StringUtils.contains(userAgent, "MSIE")) {// IE浏览器
                finalFileName = URLEncoder.encode(fileName, "UTF-8");
            } else if (StringUtils.contains(userAgent, "Mozilla")) {// google,火狐浏览器
                finalFileName = new String(fileName.getBytes("GBK"), "ISO8859-1");
            } else {
                finalFileName = URLEncoder.encode(fileName, "UTF-8");// 其他浏览器
            }
            response.setHeader("Content-Disposition", "attachment; filename=\"" + finalFileName + "\"");// 这里设置一下让浏览器弹出下载提示框，而不是直接在浏览器中打开
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
    /**
     * 
     * @param name
     * @param password
     * @param tablenames
     * @param project
     * @param packagename
     * @param targetpath
     * @return
     */
    public Context getContext(String url, String name, String password, String table, String project,
            String packagename, String targetpath, String keyid) {
        Context context = new Context(null);
        context.setTargetRuntime("MyBatis3");
        context.setId("DB2Tables");
        String domainObjectName = getDomainObjectName(table);
        TableConfiguration tabconfig = new TableConfiguration(context);
        tabconfig.setTableName(table);
        tabconfig.setDomainObjectName(domainObjectName);

        if (null != keyid && !"".equals(keyid)) {
            String column = keyid;
            boolean identity = false;
            String sqlStatement = "select SEQ_" + table.toUpperCase() + ".nextval from dual";
            String type = null;
            GeneratedKey gk = new GeneratedKey(column, sqlStatement, identity, type);
            tabconfig.setGeneratedKey(gk);
        }
        context.addTableConfiguration(tabconfig);

        CommentGeneratorConfiguration commentGeneratorConfiguration = new CommentGeneratorConfiguration();
        commentGeneratorConfiguration.addProperty("suppressDate", "true");
        commentGeneratorConfiguration.addProperty("suppressAllComments", "true");
        context.setCommentGeneratorConfiguration(commentGeneratorConfiguration);

        JDBCConnectionConfiguration jdbcConnectionConfiguration = new JDBCConnectionConfiguration();
        String dbtype = getDatabaseType(url);
        if("oracle".equals(dbtype)){
            jdbcConnectionConfiguration.setDriverClass("oracle.jdbc.driver.OracleDriver");// oracle.jdbc.driver.OracleDriver
        }else if("mysql".equals(dbtype)){
            jdbcConnectionConfiguration.setDriverClass("com.mysql.jdbc.Driver");// oracle.jdbc.driver.OracleDriver
        }
        jdbcConnectionConfiguration.setConnectionURL(url); // jdbc:oracle:thin:@127.0.0.1:1521:ETD
        if (stringHasValue(name)) {
            jdbcConnectionConfiguration.setUserId(name);
        }
        if (stringHasValue(password)) {
            jdbcConnectionConfiguration.setPassword(password);
        }
        context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);

        JavaTypeResolverConfiguration javaTypeResolverConfiguration = new JavaTypeResolverConfiguration();
        javaTypeResolverConfiguration.addProperty("forceBigDecimals", "false");
        context.setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);

        JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();
        javaModelGeneratorConfiguration.setTargetPackage(project + ".domain." + packagename.toLowerCase()); // com.un.general.domain.account
        javaModelGeneratorConfiguration.setTargetProject(targetpath);// 存放地址
        javaModelGeneratorConfiguration.addProperty("enableSubPackages", "true");
        javaModelGeneratorConfiguration.addProperty("trimStrings", "true");
        context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);

        JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();
        javaClientGeneratorConfiguration.setConfigurationType("XMLMAPPER");
        javaClientGeneratorConfiguration.setTargetPackage(project + ".dao." + packagename.toLowerCase());
        javaClientGeneratorConfiguration.setTargetProject(targetpath);
        javaClientGeneratorConfiguration.addProperty("enableSubPackages", "true");
        context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);

        SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration = new SqlMapGeneratorConfiguration();
        sqlMapGeneratorConfiguration.setTargetPackage(project + ".dao.oracle");
        sqlMapGeneratorConfiguration.setTargetProject(targetpath);
        sqlMapGeneratorConfiguration.addProperty("enableSubPackages", "true");
        context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);

        context.setTabconfig(tabconfig);
        return context;
    }

    public static String getDomainObjectName(String table) {
        if (table.toUpperCase().contains("SYS")) {
            table = table.substring(4, table.length());
        }

        if (table.toUpperCase().contains("TB")) {
            table = table.substring(3, table.length());
        }

        if (table.toUpperCase().contains("TD")) {
            table = table.substring(3, table.length());
        }

        String[] names = table.split("_");
        StringBuffer sb = new StringBuffer();
        for (String name : names) {
            sb.append(captureName(name.toLowerCase()));
        }
        return sb.toString();
    }

    public static String captureName(String name) {
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        return name;

    }
    
    private String getDatabaseType(String url) {
        if (url.contains("oracle")) {
            return "oracle";
        } else if (url.contains("mysql")) {
            return "mysql";
        }
        return null;
    }
}
